package com.duobaoyu.dby.supply.biz.service.invoice.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.duobaoyu.dby.channel.adapter.enums.ChannelCodeEnum;
import com.duobaoyu.dby.common.core.constant.StringPool;
import com.duobaoyu.dby.common.core.service.CacheTemplate;
import com.duobaoyu.dby.supply.biz.client.resource.ResourceAddressAdminClient;
import com.duobaoyu.dby.supply.biz.converter.InvoiceSettingConverter;
import com.duobaoyu.dby.supply.biz.enums.RedisKeys;
import com.duobaoyu.dby.supply.biz.service.invoice.InvoiceSettingService;
import com.duobaoyu.dby.supply.common.biz.dto.invoice.InvoiceSettingDTO;
import com.duobaoyu.dby.supply.common.biz.dto.invoice.InvoiceSettingQueryDTO;
import com.duobaoyu.dby.supply.core.common.exception.CustomAssert;
import com.duobaoyu.dby.supply.db.entity.invoice.InvoiceSetting;
import com.duobaoyu.dby.supply.db.repository.invoice.InvoiceSettingRepository;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 发票信息
 *
 * @author fengshaoyu
 * @version 1.0.0
 * @date 2023/10/12 11:18
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class InvoiceSettingServiceImpl implements InvoiceSettingService {

	private final CacheTemplate<InvoiceSetting> cacheTemplate;
	private final InvoiceSettingConverter invoiceSettingConverter;
	private final InvoiceSettingRepository invoiceSettingRepository;
	private final ResourceAddressAdminClient resourceAddressAdminClient;

	@Override
	public InvoiceSetting getInvoiceSettingCacheByChannelCode(String channelCode) {
		return cacheTemplate.findCache(RedisKeys.INVOICE.formatKey(channelCode),30, TimeUnit.DAYS,new TypeReference<InvoiceSetting>(){},() -> invoiceSettingRepository.getInvoiceSettingByChannelCodes(channelCode));
	}

	@Override
	public List<InvoiceSetting> selectList(InvoiceSettingQueryDTO invoiceSettingQuery) {
		LambdaQueryWrapper<InvoiceSetting> lambdaQueryWrapper = Wrappers.lambdaQuery(InvoiceSetting.class);
		List<InvoiceSetting> invoiceSettingList = invoiceSettingRepository.list(lambdaQueryWrapper);
		return invoiceSettingList;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean addSetting(InvoiceSettingDTO setting) {
		setting.setInvoiceRegCompanyName(StringUtils.isNotBlank(setting.getInvoiceRegCompanyName()) ? setting.getInvoiceRegCompanyName() : setting.getInvoiceCompanyName());
		List<String> areaCodes = Lists.newArrayList(Arrays.asList(setting.getInvoiceProvinceCode(), setting.getInvoiceCityCode(), setting.getInvoiceCountyCode()));
		if (StringUtils.isNotBlank(setting.getInvoiceTownCode())) {
			areaCodes.add(setting.getInvoiceTownCode());
		}
		Map<String, String> areaMap = resourceAddressAdminClient.getAreasMap(areaCodes);
		CustomAssert.isTrue(areaCodes.size() == areaMap.size(), "地址编码错误");
		List<String> channelCodeList = StrUtil.splitTrim(setting.getChannelCodes(), StringPool.COMMA);
		channelCodeList.forEach(channelCode -> {
			CustomAssert.notNull(ChannelCodeEnum.getChannelCodeEnumByCode(channelCode), "渠道不存在");
			log.warn("message:{}, data:{}", "一个渠道只能有一条开票信息", JSON.toJSONString(setting));
			CustomAssert.isTrue(Objects.isNull(invoiceSettingRepository.getInvoiceSettingByChannelCodes(channelCode)), "一个渠道只能有一条开票信息");
		});
		InvoiceSetting invoiceSetting = invoiceSettingConverter.toInvoiceSetting(setting);
		invoiceSetting.setInvoiceProvinceName(areaMap.get(setting.getInvoiceProvinceCode()));
		invoiceSetting.setInvoiceCityName(areaMap.get(setting.getInvoiceCityCode()));
		invoiceSetting.setInvoiceCountyName(areaMap.get(setting.getInvoiceCountyCode()));
		invoiceSetting.setInvoiceTownName(areaMap.get(setting.getInvoiceTownCode()));
		boolean result = invoiceSettingRepository.save(invoiceSetting);
		CustomAssert.isTrue(result,"添加开票信息失败");
		return result;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean updateSetting(InvoiceSettingDTO setting) {
		InvoiceSetting settings = invoiceSettingRepository.getById(setting.getId());
		CustomAssert.notNull(settings, "开票信息不存在");
		setting.setInvoiceRegCompanyName(StringUtils.isNotBlank(setting.getInvoiceRegCompanyName()) ? setting.getInvoiceRegCompanyName() : setting.getInvoiceCompanyName());
		List<String> areaCodes = Lists.newArrayList(Arrays.asList(setting.getInvoiceProvinceCode(), setting.getInvoiceCityCode(), setting.getInvoiceCountyCode()));
		if (StringUtils.isNotBlank(setting.getInvoiceTownCode())) {
			areaCodes.add(setting.getInvoiceTownCode());
		}
		List<String> channelCodeList = StrUtil.splitTrim(setting.getChannelCodes(), StringPool.COMMA);
		channelCodeList.forEach(channelCode -> {
			CustomAssert.notNull(ChannelCodeEnum.getChannelCodeEnumByCode(channelCode), "渠道不存在");
			log.warn("message:{}, data:{}", "一个渠道只能有一条开票信息", JSON.toJSONString(setting));
			InvoiceSetting invoiceSetting = invoiceSettingRepository.getInvoiceSettingByChannelCodes(channelCode);
			CustomAssert.isTrue(Objects.isNull(invoiceSetting) || Objects.equals(invoiceSetting.getId(),setting.getId()), "一个渠道只能有一条开票信息");
		});
		Map<String, String> areaMap = resourceAddressAdminClient.getAreasMap(areaCodes);
		CustomAssert.isTrue(areaCodes.size() == areaMap.size(), "地址编码错误");
		InvoiceSetting invoiceSetting = invoiceSettingConverter.toInvoiceSetting(setting);
		invoiceSetting.setInvoiceProvinceName(areaMap.get(setting.getInvoiceProvinceCode()));
		invoiceSetting.setInvoiceCityName(areaMap.get(setting.getInvoiceCityCode()));
		invoiceSetting.setInvoiceCountyName(areaMap.get(setting.getInvoiceCountyCode()));
		invoiceSetting.setInvoiceTownName(areaMap.get(setting.getInvoiceTownCode()));
		boolean result = invoiceSettingRepository.updateById(invoiceSetting);
		CustomAssert.isTrue(result,"修改开票信息失败");
		invoiceSettingRepository.afterCommit(() -> this.removeCache(StrUtil.splitTrim(settings.getChannelCodes(), StringPool.COMMA)));
		return result;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean deleteById(Long id) {
		InvoiceSetting settings = invoiceSettingRepository.getById(id);
		if(Objects.isNull(settings)) {
			return true;
		}
		boolean result = invoiceSettingRepository.removeById(id);
		invoiceSettingRepository.afterCommit(() -> this.removeCache(StrUtil.splitTrim(settings.getChannelCodes(), StringPool.COMMA)));
		return result;
	}

	private String getCacheKey(String channelCode) {
		return RedisKeys.INVOICE.formatKey(channelCode);
	}

	public void removeCache(String channelCode) {
		cacheTemplate.remove(this.getCacheKey(channelCode));
	}

	public void removeCache(List<String> channelCodes) {
		Set<String> keys = channelCodes.stream().map(channelCode -> this.getCacheKey(channelCode)).collect(Collectors.toSet());
		cacheTemplate.remove(keys);
	}
}
